Predicción de datos en Educación Superior

Motivación

La educación en nuestro país se ha transformado en un tema de debate, sobretodo en los últimos años, debido a la brecha que existe entre las metodologías de enseñanza impuestas por los colegios. Por otro lado, no todas las personas que terminan su etapa escolar logran entrar a la universidad, y quienes lo logran no necesariamente pueden egresar de su carrera debido a diversos factores. ¿Se puede en la realidad establecer una implicancia entre datos de la educación media y la universitaria de una persona?

Es interesante analizar cuáles son los agentes que podrían influir a la hora de terminar los estudios universitarios, pues las hipótesis no son pocas. Además, si se encuentra una relación directa con los atributos a estudiar, se podría llegar a predecir el tiempo de duración real de la carrera de una persona en específico.

Hipótesis

Generalmente se habla del tipo de colegio y notas de enseñanza media como causas importantes del éxito o fracaso al ingresar a la educación superior. ¿Es esto cierto? ¿Realmente estas variables tienen una incidencia? ¿Existen otros atributos involucrados? La hipótesis inicial es que los datos sobre el colegio donde estudió una persona y su rendimiento (por ejemplo, comuna del colegio, NEM, tipo de colegio, nivel sí influyen a la hora de determinar cuánto se demorará realmente en terminar su carrera. Si la hipótesis es cierta, entonces, como se mencionó en la sección anterior, se pueden generar predicciones. La idea general es encontrar los atributos que puedan producir algún resultado respecto al período de estudios de cada persona.

Descripción de los datos

Los datasets utilizados para estudiar todas las preguntas e hipótesis mencionadas anteriormente pertenecen al Ministerio de Educación. En particular, el primer y segundo dataset contienen los datos de todas las personas tituladas en 2015 y 2016: http://datos.mineduc.cl/dashboards/20207/descarga-bases-de-datos-titulados-de-educacion-superior/. Cada tabla está en formato csv y posee 42 columnas. Por otro lado, el tercer y cuarto dataset consisten en la información relativa a las notas de alumnos de enseñanza media entre los años 1990 y 2016 de jóvenes y adultos. Cada tabla tiene 8 columnas y también está en formato csv: http://datos.mineduc.cl/dashboards/20389/descarga-de-base-de-datos-notas-de-ensenanza-media-y-percentil-en-jovenes/.

Ejmplo de los atributos que describen los datos de las personas tituladas son los siguientes:

Hito 1

In [2]:
import pandas as pd
import plotly
import numpy as np
import sys
import seaborn as sns; sns.set(color_codes=True)
from collections import defaultdict

import os
import glob
In [3]:
input_dir = 'Titulados'
encoding = 'ISO-8859-1'
delimiter = ';'
decimal = '.'
In [4]:
def filter_file(input_path):
    original = pd.read_csv(input_path, encoding='utf-8', sep=delimiter, decimal=decimal,dtype='unicode')
    filtered = original
    return filtered
In [5]:
input_paths = glob.glob(os.path.join(input_dir, '*'))
filtered = None
output_path = None
dfs = []

for input_path in input_paths:
    print(input_path)
    dfs.append(filter_file(input_path))
    print ("listo")
Titulados/NEM_Y_PERCENTILES_JOVENES_2016_1990.csv
listo
Titulados/NEM_Y_PERCENTILES_ADULTOS_2016_1990.csv
listo
Titulados/Titulados_Educacion_Superior_2015.csv
listo
Titulados/Titulados_Educacion_Superior_2016.csv
listo
In [6]:
dfs[0].head()
Out[6]:
RBD COD_DEPE AGNO_EGRESO MRUN NEM PERCENTIL PUESTO_10 PUESTO_30
0 2311 2 2015 196 4.90 50 NO NO
1 22374 3 2013 276 5.13 70 NO NO
2 22446 3 2013 277 6.55 10 NO SI
3 5002 2 2016 332 6.65 10 SI SI
4 25753 3 2007 337 4.93 50 NO NO
In [6]:
dfs[3].head()
Out[6]:
cat_periodo codigo_unico mrun gen_alu fec_nac_alu edad_alu rango_edad año_ing_pri_año sem_ing_pri_año año_ing_carr ... ciudad_sede jornada modalidad version tipo_plan_carr carac_plan_esp area_cineunesco oecd_area oecd_subarea area_carrera_generica_n
0 TIT_2016 A2016_I100S10C10J1V1 3246683 1 199301 23 20 A 24 AÑOS 2013 1 ... PUNTA ARENAS DIURNO PRESENCIAL 1 PLAN REGULAR NO APLICA TECNOLOGÍA SERVICIOS SERVICIOS DE SEGURIDAD INGENIERÍA EN PREVENCIÓN DE RIESGOS
1 TIT_2016 A2016_I100S10C10J1V1 3752168 1 198912 27 25 A 29 AÑOS 9998 2014 ... PUNTA ARENAS DIURNO PRESENCIAL 1 PLAN REGULAR NO APLICA TECNOLOGÍA SERVICIOS SERVICIOS DE SEGURIDAD INGENIERÍA EN PREVENCIÓN DE RIESGOS
2 TIT_2016 A2016_I100S10C10J1V1 4867037 1 199405 22 20 A 24 AÑOS 2012 1 ... PUNTA ARENAS DIURNO PRESENCIAL 1 PLAN REGULAR NO APLICA TECNOLOGÍA SERVICIOS SERVICIOS DE SEGURIDAD INGENIERÍA EN PREVENCIÓN DE RIESGOS
3 TIT_2016 A2016_I100S10C10J1V1 5197286 1 199307 23 20 A 24 AÑOS 9998 2014 ... PUNTA ARENAS DIURNO PRESENCIAL 1 PLAN REGULAR NO APLICA TECNOLOGÍA SERVICIOS SERVICIOS DE SEGURIDAD INGENIERÍA EN PREVENCIÓN DE RIESGOS
4 TIT_2016 A2016_I100S10C10J1V1 5844136 1 199303 23 20 A 24 AÑOS 2012 1 ... PUNTA ARENAS DIURNO PRESENCIAL 1 PLAN REGULAR NO APLICA TECNOLOGÍA SERVICIOS SERVICIOS DE SEGURIDAD INGENIERÍA EN PREVENCIÓN DE RIESGOS

5 rows × 42 columns

En las tablas anteriores se muestran algunos datos para ciertos atributos. Los antecedentes tienen relación principalmente con el lugar de estudio e información básica del alumno titulado. A continuación se muestran los tipos de atributos para cada tabla.

Dado que ambas tablas tienen como llave MRUN del alumno, se puede generar sólo una a partir del cruce de datos, y así poder agregar más atributos a la tabla de titulados. A continuación se muestra lo anteriormente descrito:

In [7]:
#Cambiar a minusculas las columnas para poder hacer merge

dfs[0].columns = [x.lower() for x in dfs[0].columns]
dfs[1].columns=[x.lower() for x in dfs[1].columns]
dfs[2]['cat_periodo']=2015
dfs[3]['cat_periodo']=2016
In [8]:
#Concatenar las tablas de titulados
frames=[dfs[2],dfs[3]]
titulados2=pd.concat(frames,ignore_index=True)
In [9]:
frames2=[dfs[0],dfs[1]]
egresados=pd.concat(frames2,ignore_index=True)
In [10]:
# merge de tabla de egresados y titulados que va a mostrar la intersección por mrun
total=pd.merge(egresados,titulados2, on='mrun', how='inner')
In [11]:
total.head()
Out[11]:
rbd cod_depe agno_egreso mrun nem percentil puesto_10 puesto_30 cat_periodo codigo_unico ... ciudad_sede jornada modalidad version tipo_plan_carr carac_plan_esp area_cineunesco oecd_area oecd_subarea area_carrera_generica_n
0 10452 1 2009 5 4.93 90 NO NO 2016 A2016_I260S47C118J1V1 ... SANTIAGO DIURNO PRESENCIAL 1 PLAN REGULAR NO APLICA SALUD SALUD Y SERVICIOS SOCIALES MEDICINA TÉCNICO EN ENFERMERÍA
1 3247 2 2009 410 5.38 70 NO NO 2015 A2015_I31S2C46J1V1 ... TALCA DIURNO PRESENCIAL PLAN REGULAR NO APLICA SALUD SALUD Y SERVICIOS SOCIALES MEDICINA FONOAUDIOLOGÍA
2 8514 2 2013 443 5.15 90 NO NO 2016 A2016_I143S7C59J1V1 ... SANTIAGO DIURNO PRESENCIAL 1 PLAN REGULAR NO APLICA ARTE Y ARQUITECTURA SERVICIOS SERVICIOS PERSONALES TÉCNICO EN PELUQUERÍA Y ESTÉTICA
3 648 3 2007 481 5.53 60 NO NO 2016 A2016_I430S27C138J1V1 ... LA SERENA DIURNO PRESENCIAL 1 PLAN REGULAR NO APLICA TECNOLOGÍA SERVICIOS SERVICIOS DE SEGURIDAD TÉCNICO EN PREVENCIÓN DE RIESGOS
4 2455 3 2005 542 6.38 10 NO SI 2016 A2016_I70S1C178J1V1 ... SANTIAGO DIURNO PRESENCIAL 1 PLAN REGULAR NO APLICA CIENCIAS SOCIALES CIENCIAS SOCIALES, ENSEÑANZA COMERCIAL Y DERECHO CIENCIAS SOCIALES Y DEL COMPORTAMIENTO SOCIOLOGÍA

5 rows × 49 columns

Por lo que el tamaño de nuestro Data set queda:

In [12]:
total.shape
Out[12]:
(293997, 49)

Exploración de datos

Limpieza de datos

A continuación será necesario revisar si existen anomalías en los datos:

Duplicados

In [13]:
mrundup = total.mrun.value_counts()
mrundup[mrundup > 1].head()
Out[13]:
2664582     4
17740265    4
19994535    4
24280384    4
12970556    4
Name: mrun, dtype: int64

A continuación revisamos en detalle algunos mrun repetidos:

In [14]:
total.loc[total['mrun']=='1712224']
Out[14]:
rbd cod_depe agno_egreso mrun nem percentil puesto_10 puesto_30 cat_periodo codigo_unico ... ciudad_sede jornada modalidad version tipo_plan_carr carac_plan_esp area_cineunesco oecd_area oecd_subarea area_carrera_generica_n
18807 6112 3 2009 1712224 6.50 10 NO SI 2015 A2015_T_94_P_2 ... TEMUCO DIURNO PRESENCIAL PLAN REGULAR NO APLICA SALUD SALUD Y SERVICIOS SOCIALES MEDICINA POSTÍTULO EN SALUD
18808 6112 3 2009 1712224 6.50 10 NO SI 2015 A2016_T_94_P_21 ... TEMUCO DIURNO PRESENCIAL PLAN REGULAR NO APLICA SALUD SALUD Y SERVICIOS SOCIALES MEDICINA POSTÍTULO EN SALUD
18809 6112 3 2009 1712224 6.50 10 NO SI 2016 A2016_T_94_P_11 ... TEMUCO A DISTANCIA NO PRESENCIAL PLAN REGULAR NO APLICA SALUD SALUD Y SERVICIOS SOCIALES MEDICINA POSTÍTULO EN SALUD
18810 6112 3 2009 1712224 6.50 10 NO SI 2016 A2016_T_94_P_2 ... TEMUCO DIURNO PRESENCIAL PLAN REGULAR NO APLICA SALUD SALUD Y SERVICIOS SOCIALES MEDICINA POSTÍTULO EN SALUD

4 rows × 49 columns

In [15]:
total.loc[total['mrun']=='2622180']
Out[15]:
rbd cod_depe agno_egreso mrun nem percentil puesto_10 puesto_30 cat_periodo codigo_unico ... ciudad_sede jornada modalidad version tipo_plan_carr carac_plan_esp area_cineunesco oecd_area oecd_subarea area_carrera_generica_n
28887 5467 3 2010 2622180 6.25 10 NO SI 2015 A2015_T_94_P_28 ... TEMUCO DIURNO PRESENCIAL PLAN REGULAR NO APLICA HUMANIDADES HUMANIDADES Y ARTES HUMANIDADES POSTÍTULO EN HUMANIDADES
28888 5467 3 2010 2622180 6.25 10 NO SI 2016 A2016_T_94_P_33 ... TEMUCO DIURNO PRESENCIAL PLAN REGULAR NO APLICA AGROPECUARIA AGRICULTURA VETERINARIA POSTÍTULO EN AGROPECUARIA

2 rows × 49 columns

De lo anterior se puede deducir que existen algunos mrun que estan repetidos innecesariamente mienstras otros son porque el alumno estudió más de una carrera o hizo un postgrado. Ahora dado que la mayoría de los repetidos son postítulos, la alternativa más sencilla es eliminar las filas que tienen postítulos, ya que esto no es relevante en el análisis (sólo se quiere estudiar la gente que obtuvo un grado académio):

In [16]:
# filtrar todos los que  no seas postitulo
total=total.loc[total['nivel_global']!=u'POSTÍTULO']

Datos erróneos

De acuerdo a nuestra hipótesis se quiere medir los años reales que al alumno le tomó titularse, por lo que se quiere ver si existe incongruencia en la diferencia de año de ingreso a la universidad y año de titulación.

In [17]:
total[[u'año_ing_pri_año']] = total[[u'año_ing_pri_año']].astype(int)
In [18]:
columns=['cat_periodo',u'año_ing_pri_año']
In [19]:
total=total.loc[total['cat_periodo']-total[u'año_ing_pri_año']>0]

Columnas Faltantes

Ahora que no se tiene incongruencia entre los años de titulación e ingreso a la carrera se creará la columna años_carrera para que para cada alumno se tenga los años que demoró en obtener el título y la columna duración total en años, pues la actual se encuentra en semestres:

In [20]:
total['anos_carrera']=total['cat_periodo']-total[u'año_ing_pri_año']
In [21]:
total.head()
Out[21]:
rbd cod_depe agno_egreso mrun nem percentil puesto_10 puesto_30 cat_periodo codigo_unico ... jornada modalidad version tipo_plan_carr carac_plan_esp area_cineunesco oecd_area oecd_subarea area_carrera_generica_n anos_carrera
0 10452 1 2009 5 4.93 90 NO NO 2016 A2016_I260S47C118J1V1 ... DIURNO PRESENCIAL 1 PLAN REGULAR NO APLICA SALUD SALUD Y SERVICIOS SOCIALES MEDICINA TÉCNICO EN ENFERMERÍA 6
1 3247 2 2009 410 5.38 70 NO NO 2015 A2015_I31S2C46J1V1 ... DIURNO PRESENCIAL PLAN REGULAR NO APLICA SALUD SALUD Y SERVICIOS SOCIALES MEDICINA FONOAUDIOLOGÍA 4
2 8514 2 2013 443 5.15 90 NO NO 2016 A2016_I143S7C59J1V1 ... DIURNO PRESENCIAL 1 PLAN REGULAR NO APLICA ARTE Y ARQUITECTURA SERVICIOS SERVICIOS PERSONALES TÉCNICO EN PELUQUERÍA Y ESTÉTICA 2
4 2455 3 2005 542 6.38 10 NO SI 2016 A2016_I70S1C178J1V1 ... DIURNO PRESENCIAL 1 PLAN REGULAR NO APLICA CIENCIAS SOCIALES CIENCIAS SOCIALES, ENSEÑANZA COMERCIAL Y DERECHO CIENCIAS SOCIALES Y DEL COMPORTAMIENTO SOCIOLOGÍA 9
5 9347 3 2009 571 5.65 20 NO SI 2015 A2016_I17S2C41J2V1 ... VESPERTINO PRESENCIAL 1 PLAN REGULAR NO APLICA EDUCACIÓN EDUCACIÓN FORMACIÓN DE PERSONAL DOCENTE PEDAGOGÍA EN EDUCACIÓN DE PÁRVULOS 4

5 rows × 50 columns

In [22]:
total[['dur_total_carr']] = total[['dur_total_carr']].astype(int)
In [23]:
total['dur_total_anos']=total['dur_total_carr']/int(2)
In [24]:
total.head()
Out[24]:
rbd cod_depe agno_egreso mrun nem percentil puesto_10 puesto_30 cat_periodo codigo_unico ... modalidad version tipo_plan_carr carac_plan_esp area_cineunesco oecd_area oecd_subarea area_carrera_generica_n anos_carrera dur_total_anos
0 10452 1 2009 5 4.93 90 NO NO 2016 A2016_I260S47C118J1V1 ... PRESENCIAL 1 PLAN REGULAR NO APLICA SALUD SALUD Y SERVICIOS SOCIALES MEDICINA TÉCNICO EN ENFERMERÍA 6 2.5
1 3247 2 2009 410 5.38 70 NO NO 2015 A2015_I31S2C46J1V1 ... PRESENCIAL PLAN REGULAR NO APLICA SALUD SALUD Y SERVICIOS SOCIALES MEDICINA FONOAUDIOLOGÍA 4 5.5
2 8514 2 2013 443 5.15 90 NO NO 2016 A2016_I143S7C59J1V1 ... PRESENCIAL 1 PLAN REGULAR NO APLICA ARTE Y ARQUITECTURA SERVICIOS SERVICIOS PERSONALES TÉCNICO EN PELUQUERÍA Y ESTÉTICA 2 2.5
4 2455 3 2005 542 6.38 10 NO SI 2016 A2016_I70S1C178J1V1 ... PRESENCIAL 1 PLAN REGULAR NO APLICA CIENCIAS SOCIALES CIENCIAS SOCIALES, ENSEÑANZA COMERCIAL Y DERECHO CIENCIAS SOCIALES Y DEL COMPORTAMIENTO SOCIOLOGÍA 9 5.0
5 9347 3 2009 571 5.65 20 NO SI 2015 A2016_I17S2C41J2V1 ... PRESENCIAL 1 PLAN REGULAR NO APLICA EDUCACIÓN EDUCACIÓN FORMACIÓN DE PERSONAL DOCENTE PEDAGOGÍA EN EDUCACIÓN DE PÁRVULOS 4 5.0

5 rows × 51 columns

Eliminación de columnas

Si bien hay muchos atributos que son relevantes para este estudio, la dimensión de la tabla resultante no es pequeña, por lo que es probable que no todos los atributos sean imprescindibles. Así, las columnas que se pueden eliminar son las siguientes:

  • fec_nac_alu: La fecha de nacimiento del alumno no es relevante salvo para determinar la edad, pero la edad ya está contenida en una columna aparte (edad_alu).
  • año_ing_carr: Para efectos del proyecto, sólo se considerará año de ingreso a primer año (año_ing_pri_año).
  • sem_ing_carr: Para efectos del proyecto, no se considerará semestre de ingreso.
  • sem_ing_pri_año: No se considerará semestre de ingreso, además de poseer muchos filas con valores sin información.
  • fecha_obtencion_titulo: Similar a lo que ocurre con la fecha de nacimiento del alumno (sólo importa el año, cuyo valor ya está en otra columna).
  • version: Posee muy pocos valores no nulos.
  • carac_plan_esp: No entrega información relevante. Posee muchos valores nulos o sin información.

Visualización de Datos

A continuación se presentarán gráficos para ver relaciones entre los datos, outliers, etc. Primero se reemplazarán los valores null por 0 para poder graficar:

In [25]:
total=total.fillna(0)
total[['edad_alu', 'dur_estudio_carr']] = total[['edad_alu', 'dur_estudio_carr']].astype(int)
total[['nem']] = total[['nem']].astype(float)
In [26]:
import matplotlib.pyplot as plt
import pandas

names = ['edad_alu', 'dur_estudio_carr','nem','anos_carrera','dur_total_anos']
filtrado=total.filter(items=names)
filtrado.plot(kind='box', subplots=True, sharex=False, sharey=False,figsize=(20,10))
plt.show()

En la figura anterior (box plot), se muestra que en la edad de los alumnos hay valores extremos como alumnos que tienen edades mayores a 50 y edad 0 ( la cual es falta de información)

A continuación separaremos los datos por código de dependencia del colegio y se compararán con respecto al nem

In [27]:
municipales=total.loc[(total['cod_depe']=='1') | (total['cod_depe']=='2')]
particulares=total.loc[total['cod_depe']=='4']
subvencionados=total.loc[(total['cod_depe']=='3')| (total['cod_depe']=='5')]
In [28]:
subvencionados['nem'].hist(bins=20)
municipales['nem'].hist(bins=20)
particulares['nem'].hist(bins=20)
Out[28]:
<matplotlib.axes._subplots.AxesSubplot at 0x1a1beaeb70>

En el gráfico anterior se ve que los colegios subvencionados y municipales tienen similar nem. Por otro lado, los colegios privados tienen mejor nem que los subvencionados y municipales (entre 60-65).

Luego, se quiere analizar el total de matriculados por género y dependencia:

In [29]:
municipalesHombre=municipales.loc[municipales['gen_alu']=='1']
subvencionadosHombre=subvencionados.loc[subvencionados['gen_alu']=='1']
particularesHombre=particulares.loc[particulares['gen_alu']=='1']
In [30]:
municipalesMujer=municipales.loc[municipales['gen_alu']=='2']
subvencionadosMujer=subvencionados.loc[subvencionados['gen_alu']=='2']
particularesMujer=particulares.loc[particulares['gen_alu']=='2']
In [31]:
listaHombres=[34844,55098,14448]
listaMujeres=[54465,79231,15824]
# -*- coding: utf-8 -*-
import numpy as np
import matplotlib.pyplot as plt
 
# data to plot
n_groups = 3
means_franka = tuple(listaMujeres)
means_guidoa = tuple(listaHombres)
 
# create plot
fig, ax = plt.subplots(figsize=(10, 5))
index = np.arange(n_groups)
bar_width = 0.40
opacity = 0.9
 
rects1 = plt.bar(index, means_franka, bar_width,
                 alpha=opacity,
                 color='b',
                 label=u'Mujeres')
 
rects2 = plt.bar(index + bar_width, means_guidoa, bar_width,
                 alpha=opacity,
                 color='g',
                 label=u'Hombres')
 
plt.xlabel(u'Por genero')
plt.ylabel(u'Cantidad de Titulados')
plt.title(u'Cantidad de titulados por genero y dependencia')
plt.xticks(index + bar_width, (u'Municipales',u'Subvencionados',u'Particulares'))
plt.legend()


plt.tight_layout()
plt.show()

Del gráfico se deduce que el número de titulados de colegios subvencionados es significativamente mayor a las otras dependencias y el número de alumnos mujeres es mayor en todas las dependencias.

Dado esto, se decidió comparar porcentajes y comparar por tipo de institución:

In [32]:
centroFormacionTecnica=total.loc[total[u'tipo_inst_2']==u'CENTROS DE FORMACI\xd3N T\xc9CNICA']
universidadprivada=total.loc[total[u'tipo_inst_2']=='UNIVERSIDADES PRIVADAS']
institutoprofesional=total.loc[total[u'tipo_inst_2']=='INSTITUTOS PROFESIONALES']
universidadtradicional=total.loc[total[u'tipo_inst_2']=='UNIVERSIDADES CRUCH']

centroFormacionTecnicamunicipales=centroFormacionTecnica.loc[(centroFormacionTecnica['cod_depe']=='1') | (centroFormacionTecnica['cod_depe']=='2')]
centroFormacionTecnicasubvencionados=centroFormacionTecnica.loc[(centroFormacionTecnica['cod_depe']=='3') | (centroFormacionTecnica['cod_depe']=='5')]
centroFormacionTecnicaparticulares=centroFormacionTecnica.loc[centroFormacionTecnica['cod_depe']=='4']

universidadprivadamunicipales=universidadprivada.loc[(universidadprivada['cod_depe']=='1') | (universidadprivada['cod_depe']=='2')]
universidadprivadasubvencionados=universidadprivada.loc[(universidadprivada['cod_depe']=='3') | (universidadprivada['cod_depe']=='5')]
universidadprivadaprivados=universidadprivada.loc[universidadprivada['cod_depe']=='4']

institutoprofesionalmunicipales=institutoprofesional.loc[(institutoprofesional['cod_depe']=='1') | (institutoprofesional['cod_depe']=='2')]
institutoprofesionalsubvencionados=institutoprofesional.loc[(institutoprofesional['cod_depe']=='3') | (institutoprofesional['cod_depe']=='5')]
institutoprofesionalprivados=institutoprofesional.loc[institutoprofesional['cod_depe']=='4']

universidadtradicionalmunicipales=universidadtradicional.loc[(universidadtradicional['cod_depe']=='1') | (universidadtradicional['cod_depe']=='2')]
universidadtradicionalsubvencionados=universidadtradicional.loc[(universidadtradicional['cod_depe']=='3') | (universidadtradicional['cod_depe']=='5')]
universidadtradicionalprivados=universidadtradicional.loc[universidadtradicional['cod_depe']=='4']
In [33]:
listamunicipales2=[(35379/float(86771))*100,(19081/float(42517))*100,(16198/float(66430))*100,(18651/float(58209))*100]
In [34]:
listasubvencionados=[(48968/float(86771))*100,(22524/float(42517))*100,(33956/float(66430))*100,(28881/float(58209))*100]
In [35]:
listaprivados2=[(2416/float(86771))*100,(908/float(42517))*100,(16273/float(66430))*100,(10675/float(58209))*100]
In [36]:
# -*- coding: utf-8 -*-
import numpy as np
import matplotlib.pyplot as plt
 
# data to plot
n_groups = 4
means_franka = tuple(listamunicipales2)
means_guidoa = tuple(listasubvencionados)
privados= tuple(listaprivados2)
 
# create plot
fig, ax = plt.subplots(figsize=(12, 6))
index = np.arange(n_groups)
bar_width = 0.20
opacity = 0.8
 
rects1 = plt.bar(index, means_franka, bar_width,
                 alpha=opacity,
                 color='b',
                 label=u'Municipales')
 
rects2 = plt.bar(index + bar_width, means_guidoa, bar_width,
                 alpha=opacity,
                 color='g',
                 label=u'Subvencionados')

rects3 =  plt.bar(index + 2*bar_width, privados, bar_width,
                 alpha=opacity,
                 color='m',
                 label=u'Privados')
 
plt.xlabel(u'Tipo de Institución')
plt.ylabel(u'Porcentaje de titulados')
plt.title(u'Porcentaje de titulados por dependencia y tipo de institución')
plt.xticks(index + bar_width, (u'INSTITUTOS PROFESIONALES',u'CENTROS DE FORMACI\xd3N T\xc9CNICA',u'UNIVERSIDADES PRIVADAS',u'UNIVERSIDADES CRUCH'))
plt.legend()

plt.tight_layout()
plt.savefig('prueba') 
plt.show()

En el gráfico se puede ver que el porcentaje de personas tituladas provenientes de colegios particulares en institutos profesinales y centro de formación técnica es mínima. Por otro lado, los centro de formación técnica tienen similar porcentaje de alumnos de colegios municipales y privados.

Finalmente se realizó un parallel coordinates para ver las relaciones entre los atributos de la siguiente forma:

In [37]:
mydict={u'INSTITUTOS PROFESIONALES':1,
         u'UNIVERSIDADES PRIVADAS':2,
         u'CENTROS DE FORMACI\xd3N T\xc9CNICA':3,
          u'UNIVERSIDADES CRUCH':4
}
In [38]:
ans=[]
for key,value in mydict.items():
        ans.append([key,value])
In [39]:
tipoInst=pd.DataFrame(ans, columns=['tipo_inst_2','cod_tipo_inst'])
In [41]:
total1=pd.merge(total,tipoInst,on='tipo_inst_2',how='inner')
In [42]:
total2=pd.DataFrame(total1, columns=['tipo_inst_2','cod_tipo_inst'])
In [43]:
import colorlover as cl
import plotly.plotly as py
import plotly.graph_objs as go
from plotly.offline import download_plotlyjs, init_notebook_mode, plot, iplot

def configure_plotly_browser_state():
    import IPython
    display(IPython.core.display.HTML('''
        <script src="/static/components/requirejs/require.js"></script>
        <script>
          requirejs.config({
            paths: {
              base: '/static/base',
              plotly: 'https://cdn.plot.ly/plotly-latest.min.js?noext',
            },
          });
        </script>
        '''))
init_notebook_mode(connected=False)
In [44]:
colorscale = [[i,x] for i,x in enumerate(cl.scales['5']['div']['RdYlBu'])]
configure_plotly_browser_state()

total12=total1.sample(n=10000)
total12[['cod_depe']]=total12[['cod_depe']].astype(int)
data = [
    go.Parcoords(
        line = dict(color = total12['cod_depe'],
                     showscale=True),
        dimensions = list([
            dict(range = [18,70],
                label = 'Edad', values = total12['edad_alu']),
            
            dict(range = [1990,2016],
                label = 'Ano', values = total12[u'año_ing_pri_año']),
            
            dict(range = [4.0,7.0],
                label = 'Nem', values = total12['nem']),
            
            dict(tickvals = [1,2,3,4],
                 ticktext = [u'INSTITUTOS PROFESIONALES',u'UNIVERSIDADES PRIVADAS',u'CENTROS DE FORMACI\xd3N T\xc9CNICA',u'UNIVERSIDADES CRUCH'],
                 label = 'Tipo Institucion', values = total12['cod_tipo_inst']),
            
            dict(range = [0,13],
                 
                 label = 'Duracion carreras', values = total12['dur_total_anos']),
        ])
    )
]

layout = go.Layout(
    plot_bgcolor = '#E5E5E5',
    paper_bgcolor = '#E5E5E5'
)

fig = go.Figure(data = data, layout = layout)
iplot(fig, filename = 'parcoords-basic')

En el gráfico anterior el color esta mapeado al tipo de dependencia ( municipal, subvencionado,etc) y se puede apreciar que la mayoría son de color amarillo (subvencionados) y la mayoría entre 20 y 30 se tituló entre 2005 y 2015. Por otro lado la duración de las carreras parece bordear ente los 2 y 7 años en las instituciones.

Hito 2

Para este hito se partió con filtrar las columnas que creemos relevantes para la hipótesis, a continuación las columnas que elegimos

In [45]:
columns=['rbd','cod_depe','agno_egreso','mrun','nem','percentil','puesto_10','cat_periodo','codigo_unico','gen_alu','edad_alu',u'año_ing_pri_año','nomb_titulo_obtenido','otro_semestre_suspension','tipo_inst_2','nomb_inst','nivel_carrera_2','dur_estudio_carr','dur_total_carr','region_sede','oecd_area','anos_carrera','dur_total_anos']
In [46]:
tablaNueva= pd.DataFrame(total1, columns=columns)
In [47]:
tablaNueva['dur_total_carr'].max()
Out[47]:
24

Coeficiente de demora

Para calcular el coeficiente de demora que es cuánto se demora una persona efectivamente en sacar la carrera es:

In [60]:
tablaNueva['coef']=(tablaNueva['anos_carrera']-tablaNueva['dur_total_anos'])

donde anos_carrera es la columna que se creó de cuantos años efectivos esta en la carrera y "dur_total_anos" los años teóricos de la carrera.

In [61]:
tablaNueva['coef'].unique()
Out[61]:
array([ 3.5, -0.5,  0.5,  0. , -1. ,  7. ,  1. ,  3. , -1.5,  2. ,  1.5,
        2.5,  4. ,  4.5,  6. ,  5. ,  6.5, -2. ,  5.5,  9.5, -2.5,  8.5,
        9. ,  7.5,  8. , 14. , 12.5, 10. , -4.5, -3. , -3.5, -4. , -5. ,
       12. , 15. , 14.5, 17.5, 13. , 16. , 11.5, 11. , 17. ])

Luego se realizó un histograma para ver entre que números estaba el coeficiente:

In [62]:
plt.figure()
tablaNueva['coef'].plot.hist(bins=25)
Out[62]:
<matplotlib.axes._subplots.AxesSubplot at 0x1a27d61208>
In [63]:
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns
import timeit

Luego se quiso ver como era la distribución del coeficiente según universidad, para así enfocarnos en un grupo específico y evaluar el porque de la demora.

In [64]:
columns=['tipo_inst_2','coef','dur_total_anos']
reducido=pd.DataFrame(tablaNueva, columns=columns)
In [65]:
import matplotlib.pyplot as plt
bins = np.arange(0, 65, 5)
g = sns.FacetGrid(reducido,col='tipo_inst_2',margin_titles=True,height=4,col_order=["INSTITUTOS PROFESIONALES", "UNIVERSIDADES PRIVADAS","UNIVERSIDADES CRUCH","CENTROS DE FORMACIÓN TÉCNICA"])
g = g.map(plt.hist, "coef",bins=20 ,color="m")
g.savefig("graficoMineria.png")

En la visualización anterior se puede apreciar que en los institutos profesionales y centros de formación técnica la gente sale en el tiempo debido, por lo que nuestro estudio se hará en las universidades privadas y las universidades del cruch

Ahora dado que daban resultados "extraños" en el coeficiente como -4, decidimos filtrar las filas tal que el coeficiente sea mayor que -2

In [66]:
reducido=reducido.loc[reducido['coef']>-2]
In [67]:
nuevo=reducido.groupby(['tipo_inst_2']).mean()

Luego se tiene otra visualizacion de donde se encuentra el coeficiente promedio mas alto y el promedio de duración total de la carrera promedio más alto según tipo de institución:

In [68]:
cm = sns.light_palette("red", as_cmap=True)

s = nuevo.style.background_gradient(cmap=cm)
s
Out[68]:
coef dur_total_anos
tipo_inst_2
CENTROS DE FORMACIÓN TÉCNICA 0.113215 2.42632
INSTITUTOS PROFESIONALES 0.031279 2.99919
UNIVERSIDADES CRUCH 0.611375 4.6543
UNIVERSIDADES PRIVADAS 0.34298 4.54061

El resultado fue que las universidades del cruch tienen en promedio mayor coeficiente y además sus carreras duran más

Experimentos : Clasificador

In [69]:
tablaNueva=tablaNueva.loc[tablaNueva['coef']>=-1]
In [84]:
mydictionare = {
    -0.5:'A',
    0.5:'B',
    -1:'A',
    0:'A',
    1:'B',
    2.5:'B',
    1.5:'B',
    2:'B',
    3.5:'B',
    4.5:'B',
    7:'B',
    3:'B',
    4:'B',
    5:'B',
    5.5:'B',
    6.5:'B',
    6:'B',
    7.5:'B',
    8:'B',
    8.5:'B',
    12:'B',
    8.5:'B',
    12:'B',
    11.5:'B',
    9.5:'B',
    15:'B',
    9:'B',
    14.5:'B',
    10:'B',
    17.5:'B',
    13:'B',
    16:'B',
    11:'B',
    12.5:'B',
    14:'B',
    17:'B'
}
In [85]:
ans=[]
for key,value in mydictionare.items():
        ans.append([key,value])
clase=pd.DataFrame(ans, columns=['coef','clase'])
In [94]:
tablaNuevaClases=pd.merge(tablaNueva,clase,on=['coef'],how='inner')
In [96]:
reduced_data = tablaNuevaClases
import seaborn as sns
sns.set(style="ticks", color_codes=True)

g = sns.pairplot(reduced_data, vars=['nem', 'dur_total_anos', 'coef', 'anos_carrera'])
plt.show()

Para poder aplicar las técnicas de clasificación, se generó una tabla reducida donde se eliminaron las variables que se consideraba que tenían menos correlación con las clases. Así, sólo quedaron el género del alumno y la duración total de años de la carrera. De esta tabla se creó una muestra de 80000 filas debido a la demora al procesar los datos. Finalmente se mapearon las clases a tipos numéricos.

In [99]:
tablaUniversidades=tablaNuevaClases.loc[(tablaNuevaClases['tipo_inst_2']=='UNIVERSIDADES PRIVADAS') | (tablaNuevaClases['tipo_inst_2']=='UNIVERSIDADES CRUCH')]
columns=['gen_alu', 'dur_total_anos','clase']
tablaUniversidades[['gen_alu']] = tablaUniversidades[['gen_alu']].astype(int)
tablaUniversidades=pd.DataFrame(tablaUniversidades,columns=columns)
tablaPrueba=tablaUniversidades.sample(n=80000)
tablaPrueba['clase'] = tablaPrueba['clase'].map({'A':1, 'B':3})
tablaPrueba = tablaPrueba.dropna()
array = tablaPrueba.values
/Users/vale/anaconda3/lib/python3.7/site-packages/pandas/core/frame.py:3140: SettingWithCopyWarning:


A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-view-versus-copy

Finalmente aquí se comienza a aplicar clasificación, partiendo con Árbol de Decisión. Los datos de entrenamiento corresponden al 70% de la muestra. Además, se evaluó el desempeño mediante la tabla de métricas y se contaron la cantidad de clases (a pesar de ser una selección aleatoria de la muestra siempre daba una proporción similar)

Árbol de Decisión

In [100]:
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.metrics import f1_score, recall_score, precision_score
from sklearn.tree import DecisionTreeClassifier
X = array[:,0:2]
y= array[:,2]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=.30,train_size=0.70, stratify=y)
clf = DecisionTreeClassifier()
clf.fit(X_train, y_train)  
predictions=clf.predict(X_test)

from sklearn.metrics import accuracy_score, classification_report
print(classification_report(y_test,predictions))
accuracy_score(y_test, predictions)

tablaUniversidades['clase'].value_counts()
             precision    recall  f1-score   support

        1.0       0.62      0.80      0.70     13666
        3.0       0.57      0.34      0.42     10334

avg / total       0.59      0.60      0.58     24000

Out[100]:
A    68494
B    51902
Name: clase, dtype: int64

Con su respectiva matriz de confución

In [101]:
from sklearn.metrics import confusion_matrix
cm = confusion_matrix(y_test, predictions)
plt.matshow(cm)
plt.colorbar()
plt.ylabel('True label')
plt.xlabel('Predicted label')
Out[101]:
Text(0.5,0,'Predicted label')

k-fold

In [102]:
from sklearn import metrics, model_selection

print("\nPREDICTIONS******")
predictions = model_selection.cross_val_predict(clf, X_test, y_test, cv=10)  ## cv es la cantidad de folds
print("Accuracy:", metrics.accuracy_score(y_test, predictions))
print("Metricas:")
print(metrics.classification_report(y_test, predictions))
PREDICTIONS******
Accuracy: 0.6024166666666667
Metricas:
             precision    recall  f1-score   support

        1.0       0.61      0.82      0.70     13666
        3.0       0.57      0.32      0.41     10334

avg / total       0.59      0.60      0.58     24000

KNN

In [103]:
# Para cargar KNN
from sklearn.neighbors import KNeighborsClassifier
K = 5 # numero de vecinos
knn = KNeighborsClassifier(n_neighbors=K)  

## AGREGUE CODIGO PREGUNTA 5.2
knn.fit(X, y) 
y_pred2 = knn.predict(X_test) 
print("Accuracy:", metrics.accuracy_score(y_test,y_pred2))
print(metrics.classification_report(y_test, y_pred2))
Accuracy: 0.5510416666666667
             precision    recall  f1-score   support

        1.0       0.61      0.57      0.59     13666
        3.0       0.48      0.53      0.50     10334

avg / total       0.56      0.55      0.55     24000

Naive Bayes

In [104]:
from sklearn.naive_bayes import GaussianNB  # naive bayes
clf = GaussianNB()
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=.30,train_size=0.70, stratify=y)
clf.fit(X_train, y_train)
predictions = clf.predict(X_test)

print("Accuracy:", metrics.accuracy_score(y_test, predictions))
print(metrics.classification_report(y_test, predictions))
Accuracy: 0.5862083333333333
Accuracy: 0.5786666666666667
             precision    recall  f1-score   support

        1.0       0.61      0.71      0.66     13666
        3.0       0.51      0.40      0.45     10334

avg / total       0.57      0.58      0.57     24000

Experimentos hito 3

Para este hito, decidimos agregar atributos a nuestra base de datos. Dado que que poseemos el rbd del colegio se pudo obtener la región y la comuna de este. Con datos de http://junaebabierta.junaeb.cl/ , se pudo obtener el índice de vulnerabilidad del colegio. Se filtraron alumnos que estudiaron en una universidad del cruch (cantidad de alumnos que terminaron su carrera a tiempo y los que no, pues en este caso las clases estan balanceadas).

In [115]:
input_dir = 'hito3'
encoding = 'ISO-8859-1'
delimiter = ','
decimal = '.'
In [116]:
def filter_file(input_path):
    original = pd.read_csv(input_path, encoding=encoding, sep=delimiter, decimal=decimal,dtype='unicode')
    filtered = original
    return filtered
In [117]:
input_paths = glob.glob(os.path.join(input_dir, '*'))
filtered = None
output_path = None
dfs = []

for input_path in input_paths:
    print(input_path)
    dfs.append(filter_file(input_path))
    print ("listo")
hito3/vale (2).csv
listo
In [118]:
total = dfs[0]
In [120]:
tablaNueva=total
In [121]:
ans=[]
for key,value in mydictionare.items():
        ans.append([key,value])
clase=pd.DataFrame(ans, columns=['coef','clase'])
In [122]:
tablaNueva[['coef']] = tablaNueva[['coef']].astype(float)
In [123]:
tablaNuevaClases=pd.merge(tablaNueva,clase,on=['coef'],how='inner')
In [124]:
tablaNuevaClases[['cod_reg_rbd']] = tablaNuevaClases[['cod_reg_rbd']].astype(int)
tablaNuevaClases[['% ive-sinae media 2016']] = tablaNuevaClases[['% ive-sinae media 2016']].astype(float)
In [125]:
tablaUniversidades=tablaNuevaClases.loc[(tablaNuevaClases['tipo_inst_2']=='UNIVERSIDADES PRIVADAS') | (tablaNuevaClases['tipo_inst_2']=='UNIVERSIDADES CRUCH')]
columns=['gen_alu', 'dur_total_anos', 'cod_reg_rbd','clase']
tablaUniversidades[['gen_alu']] = tablaUniversidades[['gen_alu']].astype(int)
tablaUniversidades[['dur_total_anos']] = tablaUniversidades[['dur_total_anos']].astype(float)
tablaUniversidades=pd.DataFrame(tablaUniversidades,columns=columns)
tablaPrueba=tablaUniversidades.sample(n=50000)
tablaPrueba['clase'] = tablaPrueba['clase'].map({'A':1, 'B':3})
tablaPrueba = tablaPrueba.dropna()
array = tablaPrueba.values

Árbol de Decisión

In [126]:
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.metrics import f1_score, recall_score, precision_score
from sklearn.tree import DecisionTreeClassifier
X = array[:,0:3]
y= array[:,3]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=.30,train_size=0.70, stratify=y)
clf = DecisionTreeClassifier()
clf.fit(X_train, y_train)  
predictions=clf.predict(X_test)

from sklearn.metrics import accuracy_score, classification_report
print(classification_report(y_test,predictions))
accuracy_score(y_test, predictions)

tablaUniversidades['clase'].value_counts()
             precision    recall  f1-score   support

        1.0       0.63      0.67      0.65      7898
        3.0       0.61      0.56      0.58      7102

avg / total       0.62      0.62      0.62     15000

Out[126]:
A    27015
B    24365
Name: clase, dtype: int64

k-fold

In [127]:
from sklearn import metrics, model_selection

print("\nPREDICTIONS******")
predictions = model_selection.cross_val_predict(clf, X_test, y_test, cv=10)  ## cv es la cantidad de folds
print("Accuracy:", metrics.accuracy_score(y_test, predictions))
print("Metricas:")
print(metrics.classification_report(y_test, predictions))
PREDICTIONS******
Accuracy: 0.6153333333333333
Metricas:
             precision    recall  f1-score   support

        1.0       0.62      0.69      0.65      7898
        3.0       0.61      0.53      0.57      7102

avg / total       0.61      0.62      0.61     15000

KNN

In [128]:
# Para cargar KNN
from sklearn.neighbors import KNeighborsClassifier
K = 5 # numero de vecinos
knn = KNeighborsClassifier(n_neighbors=K)  

## AGREGUE CODIGO PREGUNTA 5.2
knn.fit(X, y) 
y_pred2 = knn.predict(X_test) 
print("Accuracy:", metrics.accuracy_score(y_test,y_pred2))
print(metrics.classification_report(y_test, y_pred2))
Accuracy: 0.5827333333333333
             precision    recall  f1-score   support

        1.0       0.60      0.60      0.60      7898
        3.0       0.56      0.56      0.56      7102

avg / total       0.58      0.58      0.58     15000

Naive Bayes

In [129]:
from sklearn.naive_bayes import GaussianNB  # naive bayes
clf = GaussianNB()
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=.30,train_size=0.70, stratify=y)
clf.fit(X_train, y_train)
predictions = clf.predict(X_test)

print("Accuracy:", metrics.accuracy_score(y_test, predictions))
print(metrics.classification_report(y_test, predictions))
Accuracy: 0.5862
             precision    recall  f1-score   support

        1.0       0.60      0.63      0.61      7898
        3.0       0.57      0.54      0.55      7102

avg / total       0.59      0.59      0.59     15000

Random Forest

In [130]:
from sklearn.ensemble import RandomForestClassifier
clf = RandomForestClassifier(n_estimators=100)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=.30,train_size=0.70, stratify=y)
clf.fit(X_train, y_train)
predictions = clf.predict(X_test)

print("Accuracy:", metrics.accuracy_score(y_test, predictions))
print(metrics.classification_report(y_test, predictions))
/Users/vale/anaconda3/lib/python3.7/site-packages/sklearn/ensemble/weight_boosting.py:29: DeprecationWarning:

numpy.core.umath_tests is an internal NumPy module and should not be imported. It will be removed in a future NumPy release.

Accuracy: 0.6234666666666666
             precision    recall  f1-score   support

        1.0       0.63      0.69      0.66      7898
        3.0       0.61      0.55      0.58      7102

avg / total       0.62      0.62      0.62     15000

In [131]:
cm = confusion_matrix(y_test, predictions)
plt.matshow(cm)
plt.colorbar()
plt.ylabel('True label')
plt.xlabel('Predicted label')
Out[131]:
Text(0.5,0,'Predicted label')

Nuestro clasificador no tuvo buenos resultados bajo ningún método solo mejoró un 1% (60% aprox) el rendimiento con respecto al hito 2, por lo que se intentó otra técnica de Minería de Datos.

Reglas de asociación

Se buscaron reglas de asociación en el dataset, para así obtener diferencias entre los alumnos que terminaron a tiempo y los que no. Primero se categorizaron las columnas a utilizar y luego se dividió el dataset entre los que terminaron a tiempo y los que no, para hacer un estudio comparativo. Se escogieron las columnas: cod_depe, rango_nem, gen_alu, oecd_area, rango_edad, cod_reg e índice para hacer los experimentos y se utilizo el algorimto apriori.

Primer experimento

En este experimento se consideraron a los alumnos que salieron a tiempo y solo se consideraon el rango_nem, gen_alu, oecd_are, rango_edad y cod_reg y se utilizó el algoritmo apriori con un confidence mínimo del 0,03 y support mínimo del 0,01 :

En las reglas anteriores podemos notar un alto confidence y lift lo que demuestra que los itemsets estan altamente relacionados. Por otro lado, notamos que el support da bajo lo que puede ser resultado de que el dataset es grande (26 mil registros). Luego se ve que las reglas tienen que ver con el genero del alumno, por ejemplo: cod_depe=4 (colegio privado) y educación estan relacionados con gen_alu 2 que es ser mujer.

Luego se hizo lo mismo para el grupo de alumnos con coeficiente mayor a cero y se obtuvo:

De lo anterior se concluye que no hay diferencia en las reglas del primer y segundo grupo

Segundo Experimento

En este caso quitamos el rango_nem y agregamos el indice de vulnerabilidad del colegio y se obtiene:

En este experimento mejoró considerablemente el lift y se obtuvieron reglas sobre las regiones, por ejemplo: La región de antofagasta e indice de vulnerabilidad medio esta altamente relacionado con el cod_depe 1 que es colegio municipal.

A continuación graficamos para observar si se reflejan las reglas de asociación:

En este gráfico se puede observar que efectivamente las mujeres estudian más carreras de educación y los hombres de ingeniería.

En ese gráfico se muestra que la región de Arica y Parinacota tiene un coef de demora considerablemente mayor que la otras regiones los que nos dice que podemos examinar el data set de forma más especificas para hacer nuestro estudio

Tercer experimento

El último experimento consistió en filtrar los colegios municipales tradicionales (Carmela Carvajal e Instituto nacional) y alumnos de colegios privados de excelencia (Nido de aguila, colegio Internacional Alba, etc)

y se obtuvieron los siguientes resultados respectivamente:

Conclusiones

Del trabajo realizado podemos obtener las siguientes conlusiones:

  1. Factores como el NEM o índice de vulnerabilidad del establecimiento escolar no demostraron ser determinantes como se creía, en efecto, cuando se sacaba el nem se obtenían mejores resultados.
  2. Se muestra una preferencia de carreras distinguidas por género, las mujeres estudian carreras de educación y los hombres carreras de ingeniería lo que es esperable.
  3. Se pueden considerar otros atributos para el clasificador, ya que los que consideramos no dieron buenos resultados, nuestro clasificador tuvo mal rendimiento con todas las métricas creemos que usar parámetros por default pudo haber afectado el rendimiento.

Trabajo Futuro

Considerar un universo más amplio y con menos sesgo, con clases más distinguibles entre sí, por ejemplo: personas tituladas y no tituladas y predecir quiénes tienes más posibilidades de desertar.

Por otro lado, se puede reducir el universo a estudiar, es decir, estudiar por carrera o región el coeficiente de demora pues así se pueden notar diferencias más marcadas.

Grupo Proyecto

  1. Tamara Gutierrez
  2. Valentina Urzúa